<?php

namespace Tyrone\Lib;


class SingleWorker extends BaseWorker
{
    private $platform;
    private $plat_server;
    private $crontab_list = [];
    private $logCenter = false;
    const CENTER_DATABASE = 'log_center';
    private $serverData = [];


    private $servers;
    private $crontab;
    private $game_crontab;
    private $center_crontab;

    private $GameMysqlPoolInfo;
    private $DefaultMysqlPools;
    private $DefaultInsertMysqlPools;
    private $uniqueKey;
    private $centerChannel;

    //$allServers,$crontab_configs,$center_crontab_configs
    //$mysqlPools,$gameMysqls,$info


    public function setSid($platform, $plat_server)
    {
        $this->platform = $platform;
        $this->plat_server = $plat_server;
        return $this;
    }

    public function setCenter($status)
    {
        $this->logCenter = $status;
        return $this;
    }

    public function setCrontabList($crontab_list)
    {
        $this->crontab_list = $crontab_list;
        return $this;
    }


    public function initCrontabTask($server, $index = 1, $callback = null)
    {
        $tasks = [];
        foreach ($server->serverData[$index] as $crontab_config) {
            $params = $crontab_config['params'] ?? [];
            unset($crontab_config['params']);
            $tasks[] = $crontab_config + [
                    'callback' => $callback,
                    'last_run' => 0,
                    'params' => getParams($params),
                ];
        }
        return $tasks;
    }

    public function isRunCenterCrontab($worker_id)
    {
        return (GROUP == 1 && $worker_id === 0);
    }


    public function init(&$server, $worker_id, $conf)
    {
        $this->loadBaseFile($conf);

        $this->worker_id = $worker_id;
        $this->MysqlPool = $this->creatMysqlPool($conf, 'log_center', 1);//先创建1个


        $center_database = self::CENTER_DATABASE;
        $baseParams = ['config' => $this->config];

        //每天刷新数据
        $server->serverData = $this->load_config();//全部数据
        $this->servers = $this->splitServer($server);
        $this->RedisPool = $this->initDefaultRedisPool($conf);
        $this->DefaultMysqlPools = $this->initDefaultMysqlPool($conf);
        $this->GameMysqlPoolInfo = $this->initGameMysqlPool();

        $callback = function ($crontab_config, $database, $gamePoolKey) use ($baseParams) {
            $info = $this->GameMysqlPoolInfo[2][$database] ?? [];
            $runParams = ['params' => $crontab_config['params'] ?? []];
            $params = array_merge($baseParams, $runParams) + $info;
            $this->run($crontab_config, $database, $gamePoolKey, $params);
        };

        if ($worker_id !== -1) {
            $this->initCenterInsertGameData($server, $worker_id, $conf);
            $this->crontab = new \Tyrone\Lib\Crontab();
            $this->crontab->setRunNum([$center_database => 1]);
            $this->game_crontab = new \Tyrone\Lib\Crontab();
            $this->center_crontab = new \Tyrone\Lib\Crontab();
            $this->game_crontab->setTask($this->initCrontabTask($server, 1, $callback))->setRunNum($this->GameMysqlPoolInfo[1]);
            $this->center_crontab->setTask($this->initCrontabTask($server, 2, $callback))->setRunNum([$center_database => 1]);

            $this->crontab->add('0 0 * * *', function () use ($conf, $worker_id, $server, $callback, $center_database) {
                $server->serverData = $this->load_config();
                $this->servers = $this->splitServer($server);
                $this->DefaultMysqlPools = $this->initDefaultMysqlPool($conf);
                $this->GameMysqlPoolInfo = $this->initGameMysqlPool();
                $this->game_crontab->setTask($this->initCrontabTask($server, 1, $callback))->setRunNum($this->GameMysqlPoolInfo[1]);
                $this->center_crontab->setTask($this->initCrontabTask($server, 2, $callback))->setRunNum([$center_database => 1]);
            });
            $server->ticks[$worker_id][] = $this->crontab->start();
        }


        if ($worker_id !== -1) {
            $server->ticks[$worker_id][] = $this->game_crontab->start();
            if ($this->isRunCenterCrontab($worker_id)) {
                $server->ticks[$worker_id][] = $this->center_crontab->start();
            }

        } else {
            if ($this->logCenter) {
                foreach ($server->serverData[2] as $crontab_config) {
                    $result = $callback($crontab_config, $center_database, 1);
                    return $result;
                }
            }
            foreach ($this->GameMysqlPoolInfo[1] as $database => $gamePoolKey) {
                foreach ($server->serverData[1] as $crontab_config) {
                    $result = $callback($crontab_config, $database, $gamePoolKey);
                    return $result;
                }
                break;
            }


        }

    }

    public function load_config()
    {
        $database = $this->MysqlPool->get();
        $map = ['group' => GROUP];//
        if ($this->platform) {
            $map['platform'] = $this->platform;
        }
        if ($this->plat_server) {
            $map['plat_server'] = $this->plat_server;
        }
        $mysql_configs = $database->select("s_game_db_config", [
            "platform",
            "plat_server",
            "mysql_host(host)",
            "mysql_port(port)",
            "mysql_user(username)",
            "mysql_pass(password)",
        ], $map);
        if ($this->crontab_list) {
            $center_crontab_list = $crontab_list = $this->crontab_list;
        } else {
            $lists = require DIR . '/data/crontab.php';
            $crontab_list = $lists['game'];
            $center_crontab_list = $lists['center'];
        }


        $this->MysqlPool->put($database);
        return [$mysql_configs, $crontab_list, $center_crontab_list];
    }

    public function splitServer($server)
    {
        $allServers = $server->serverData[0];
        if ($this->worker_id === -1) return $allServers;
        /*//分片服务器
        $DCS = array_chunk($allServers,GROUP_SUM);
        $allServers = $DCS[GROUP - 1] ?? [];*/
        $totalWorkers = $server->setting['worker_num'];
        return array_values(array_filter($allServers, function ($srv) use ($totalWorkers) {
            $instanceKey = "{$srv['platform']}:{$srv['host']}";
            $hash = crc32($instanceKey); // 生成哈希
            return ($hash % $totalWorkers) === $this->worker_id;
        }));
    }

    protected function dbLimit()
    {
        $max = defined('LOG_DB_LIMIT') ? LOG_DB_LIMIT : 500;
        $max = min($max, 500);
        $num = $this->servers ? intval(count($this->servers) * CENTER_POOL_TIMES) : 1;
        return min($num, $max);
    }

    public function initDefaultMysqlPool($conf, $num = null)
    {

        return $this->creatMysqlPool($conf, 'log_center', $num ?: $this->dbLimit());
    }

    public function initDefaultOldMysqlPool($conf)
    {
        return $this->creatMysqlPool($conf, 'center', $this->dbLimit());
    }

    public function initDefaultRedisPool($conf)
    {
        return $this->creatRedisPool($conf, 10);
        //return  $this->creatRedisPool($conf,'log_center',$this->servers ? intval(count($this->servers) * CENTER_POOL_TIMES) : 1);
    }

    public function initGameMysqlPool()
    {
        $Pools = [];
        $dbPoolEnum = [];
        $groupConfig = [];
        $info = [];
        $group = [];
        $myServers = $this->servers;
        foreach ($myServers as $srv) {
            $database = getGameDb($srv['platform'], $srv['plat_server']);
            $uniqueKey = "{$srv['host']}:{$srv['port']}:{$srv['username']}:" . md5($srv['password']);
            if (!isset($group[$uniqueKey])) {
                $group[$uniqueKey] = 1;
                $groupConfig[$uniqueKey] = $srv + compact('database');
            } else {
                $group[$uniqueKey]++;
            }
            $dbPoolEnum[$database] = $uniqueKey;
            $info[$database] = ['platform' => $srv['platform'], 'plat_server' => $srv['plat_server']] + ['worker_id' => $this->worker_id];//其他信息
        }
        foreach ($group as $uniqueKey => $num) {
            $Pools[$uniqueKey] = new \Tyrone\Lib\DatabasePool($groupConfig[$uniqueKey], $num, ['debug' => APP_DEBUG]);
        }
        return [$Pools, $dbPoolEnum, $info];
    }


    public function run($crontab_config, $database, $gamePoolKey, $params)
    {
        $class_name = $crontab_config['class'];
        $method = $crontab_config['method'];

        echo 'start_run-----' . "{$class_name}:{$method}==" . date('Y-m-d H:i:s') . '---' . $database . PHP_EOL;

        $old_mysql = $game_mysql = $redis = $mysql = null;
        try {
            $mysql = $this->DefaultMysqlPools->get();
            //$old_mysql = $this->DefaultOldMysqlPools->get();
            $redis = $this->RedisPool->get();
            $index = rand(0,count($this->centerChannel)-1);
            $channel = $this->centerChannel[$index];
            if ($database != self::CENTER_DATABASE) {
                $gameMysqlPool = $this->GameMysqlPoolInfo[0][$gamePoolKey] ?? null;
                $game_mysql = $gameMysqlPool ? $gameMysqlPool->get() : null;
                $sql = "USE `{$database}`";
                $PDOStatement = $game_mysql->query($sql);
                if (!$PDOStatement) {
                    echo "USE DB failed: {$game_mysql->error}\n";
                    //return;
                }
            }
            defer(function () use ($mysql, $redis, $game_mysql, $old_mysql, $gamePoolKey) {
                if ($game_mysql && $this->GameMysqlPoolInfo[0][$gamePoolKey]) {
                    $this->GameMysqlPoolInfo[0][$gamePoolKey]->put($game_mysql); // 归还连接
                }
                if ($mysql && $this->DefaultMysqlPools) {
                    $this->DefaultMysqlPools->put($mysql); // 归还连接
                }
                if ($old_mysql && $this->DefaultMysqlPools) {
                    $this->DefaultOldMysqlPools->put($old_mysql); // 归还连接
                }
                if ($redis && $this->RedisPool) {
                    $this->RedisPool->put($redis); // 归还连接
                }
            });

            $params += compact('mysql', 'redis', 'game_mysql', 'database', 'old_mysql', 'channel');
            $params['conf'] = $this->conf;
            if ($this->sDate) $params['sDate'] = $this->sDate;
            if ($this->eDate) $params['eDate'] = $this->eDate;
            //$params += ['game_mysql'=>null,'database'=>$database];
            //print_r($params);
            /* @var $class \Tyrone\Command\Base */
            $class = new $class_name($this->RedisPool, $params);
            //echo "开始执行{$mysqlKey}:{$database}:{$class_name}:{$method}的\n";
            $result = $class->{$method}();
            //$result = 'test';
            echo 'end_run-------' . "{$class_name}:{$method}==" . date('Y-m-d H:i:s') . "=={$gamePoolKey}:{$database}:{$class_name}:{$method}:result:({$result})的\n";
            //print_r($game_mysql->log());
            //print_r($mysql->log());
            //$result = 'test';

            //print_r($params);
        } catch (\Throwable $throwable) {
            echo "Task error: " . $throwable->getMessage() . "\n";
            echo "Trace: " . $throwable->getTraceAsString() . "\n";
            echo "Trace: " . $throwable->getLine() . "\n";
            //echo "执行{$mysqlKey}:{$database}:{$class_name}:{$method}---失败---\n";
        } finally {

        }
        return $result ?? null;
    }

    public function initCenterInsertGameData($server, $worker_id, $conf)
    {

        $flushInterval = 2;
        $num = 10;
        $this->DefaultInsertMysqlPools = $this->initDefaultMysqlPool($conf, 5*$num);
        foreach (range(0, $num-1) as $i) {
            $this->centerChannel[$i] = new \Swoole\Coroutine\Channel(1000);
            \Swoole\Coroutine::create(function () use ($conf, $flushInterval,$worker_id,$i) {
                while (true) {
                    // 尝试从队列取数据，最多等待 $flushInterval 秒
                    $data = $this->centerChannel[$i]->pop($flushInterval);
                    echo date('Y-m-d H:i:s') .'_'.$worker_id.'_'.$i.PHP_EOL;
                    if ($data === false) {
                    } else {
                        $mysql = null;
                        try {
                            $class_name = $data['class'];
                            $method = $data['method'];
                            $class = new $class_name($this->RedisPool, []);
                            $params = $data['params'];
                            $data = $data['data'];
                            $param = [];
                            $mysql = $this->DefaultInsertMysqlPools->get();

                            defer(function () use ($mysql) {
                                if ($mysql && $this->DefaultInsertMysqlPools) {
                                    $this->DefaultInsertMysqlPools->put($mysql);
                                }
                            });
                            foreach ($params as $value) {
                                $value == 'db' && $value = $mysql;
                                $value == 'data' && $value = $data;
                                $param[] = $value;
                            }
                            print_r($param);
                            $result = $class->{$method}(...$param);
                        } catch (\Throwable $exception) {

                        } finally {

                        }

                    }
                }
            });
        }
    }
}